/*
 * lieditor
 * image editor using Linux terminal
 *
 * - saves into non-compressed bmp format
 * - uses 8 colors
 * - works in Linux terminals (80x24 window is preferred)
 *
 * author: László Ádám
 * e-mail: adam.laszlo.91@gmail.com
 * 2014. 1. august
 * version: 1.1
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "linuxstuff.h"
#include "stuff.h"

//  global variables  //
int size_x, size_y;
int actual_color = BLACK;
char **map;
int x = 0, y = 0;
int continous = 0;

//  bottom  //
void bottom() {
	gotoxy(1, 22);
	printf("move: w-a-s-d|\tdot: ENTER|\trubber: q|\tcolors: r|\ncontinous drawing: e|\nsave: m|\tleave: l|");
}

//  rightside  //
void rightside() {
	int i;
	for (i = 3; i <= 17; i = i + 2) {
		gotoxy(72, i);
		backgroundcolor((i - 3) / 2);
		printf("   ");
		if ((i - 3) / 2 == actual_color) {
			defaultcolors();
			printf(" X");
		}
	}
	defaultcolors();
	if (continous) {
		gotoxy(72, 19);
		printf("cont.");
	}
	gotoxy(72, 21);
	printf("%d x %d", x, y);
	gotoxy(72, 22);
	printf("%d x %d", size_x, size_y);
	
	gotoxy(80, 24);

}

//  rotatecolor  //
void rotatecolor() {
	actual_color++;
	if (actual_color == 8) {
		actual_color = 0;
	}
}

//  mapdrawer  //
void mapdrawer() {
	int i, k;
	gotoxy(1, 1);
	for (i = 0; i < size_y; i++) {
		textcolor(BLACK); // white looks better, but does not work visually on every terminal
		for (k = 0; k < size_x; k++) {
			backgroundcolor(map[i][k]);
			if (x == i && y == k) {
				putchar('X');
			} else {
				putchar(' ');
			}
		}
		defaultcolors();
		putchar('|');
		putchar('\n');
	}
	for (i = 0; i <= size_x; i++) {
		putchar('-');
	}
}

//  save  //
void save(char *name) {
	clearscr();
	int i, k, l;
	FILE *out;
	typedef struct {
		int sign[2];
		int size[4];
		int free[4];
		int bitmap_start[4];
		int inf_header[4];
		int x[4];
		int y[4];
		int show[2];
		int color_depth[2];
		int compress[4];
		int bitmap_size[4];
		int ppm_x[4];
		int ppm_y[4];
		int palette[4];
		int palette_colors[4];
	} bitmap_struct;
	bitmap_struct bitmap;

	// BM
	bitmap.sign[1] = 'M';
	bitmap.sign[0] = 'B';
	// size
	int size = 54 + (size_x + (size_x % 4)) * (size_y + (size_y % 4));
	bitmap.size[3] = size / (256 * 256 * 256);
	bitmap.size[2] = (size % (256 * 256 * 256)) / (256 * 256);
	bitmap.size[1] = (size % (256 * 256)) / (256);
	bitmap.size[0] = size % 256;
	// unused
	bitmap.free[3] = 0;
	bitmap.free[2] = 0;
	bitmap.free[1] = 0;
	bitmap.free[0] = 0;
	// offset for bitmap data
	bitmap.bitmap_start[3] = 0;
	bitmap.bitmap_start[2] = 0;
	bitmap.bitmap_start[1] = 0;
	bitmap.bitmap_start[0] = 54;
	// BID
	bitmap.inf_header[3] = 0;
	bitmap.inf_header[2] = 0;
	bitmap.inf_header[1] = 0;
	bitmap.inf_header[0] = 40;
	// x size
	int x_size = size_x;
	bitmap.x[3] = x_size / (256 * 256 * 256);
	bitmap.x[2] = (x_size % (256 * 256 * 256)) / (256 * 256);
	bitmap.x[1] = (x_size % (256 * 256)) / (256);
	bitmap.x[0] = x_size % 256;
	// y size
	int y_size = size_y;
	bitmap.y[3] = y_size / (256 * 256 * 256);
	bitmap.y[2] = (y_size % (256 * 256 * 256)) / (256 * 256);
	bitmap.y[1] = (y_size % (256 * 256)) / (256);
	bitmap.y[0] = y_size % 256;
	// number of color planes
	bitmap.show[1] = 0;
	bitmap.show[0] = 8;
	// bits per pixel
	bitmap.color_depth[1] = 0;
	bitmap.color_depth[0] = 24;
	// compression type
	bitmap.compress[3] = 0;
	bitmap.compress[2] = 0;
	bitmap.compress[1] = 0;
	bitmap.compress[0] = 0;
	// ??
	bitmap.bitmap_size[3] = 0;
	bitmap.bitmap_size[2] = 0;
	bitmap.bitmap_size[1] = 0;
	bitmap.bitmap_size[0] = 0;
	// pixel per meter x
	bitmap.ppm_x[3] = 0;
	bitmap.ppm_x[2] = 0;
	bitmap.ppm_x[1] = 46;
	bitmap.ppm_x[0] = 224;
	// pixel per meter y
	bitmap.ppm_y[3] = 0;
	bitmap.ppm_y[2] = 0;
	bitmap.ppm_y[1] = 46;
	bitmap.ppm_y[0] = 224;
	// blah blah
	bitmap.palette[3] = 0;
	bitmap.palette[2] = 0;
	bitmap.palette[1] = 0;
	bitmap.palette[0] = 0;
	// blah blah
	bitmap.palette_colors[3] = 0;
	bitmap.palette_colors[2] = 0;
	bitmap.palette_colors[1] = 0;
	bitmap.palette_colors[0] = 0;
	out = fopen(name, "wb");

	// write the data into the file
	for (i = 0; i <= 1; i++) {
		fputc(bitmap.sign[i], out);
	}
	for (i = 0; i <= 3; i++) {
		fputc(bitmap.size[i], out);
	}
	for (i = 0; i <= 3; i++) {
		fputc(bitmap.free[i], out);
	}
	for (i = 0; i <= 3; i++) {
		fputc(bitmap.bitmap_start[i], out);
	}
	for (i = 0; i <= 3; i++) {
		fputc(bitmap.inf_header[i], out);
	}
	for (i = 0; i <= 3; i++) {
		fputc(bitmap.x[i], out);
	}
	for (i = 0; i <= 3; i++) {
		fputc(bitmap.y[i], out);
	}
	for (i = 0; i <= 1; i++) {
		fputc(bitmap.show[i], out);
	}
	for (i = 0; i <= 1; i++) {
		fputc(bitmap.color_depth[i], out);
	}
	for (i = 0; i <= 3; i++) {
		fputc(bitmap.compress[i], out);
	}
	for (i = 0; i <= 3; i++) {
		fputc(bitmap.bitmap_size[i], out);
	}
	for (i = 0; i <= 3; i++) {
		fputc(bitmap.ppm_x[i], out);
	}
	for (i = 0; i <= 3; i++) {
		fputc(bitmap.ppm_y[i], out);
	}
	for (i = 0; i <= 3; i++) {
		fputc(bitmap.palette[i], out);
	}
	for (i = 0; i <= 3; i++) {
		fputc(bitmap.palette_colors[i], out);
	}

	// here is the image itself beging written into the file
	for (i = size_y - 1; i >= 0; i--) {
		for (k = 0; k < size_x; k++) {
			switch (map[i][k]) {
			case BLACK:
				putc(0, out);
				putc(0, out);
				putc(0, out);
				break;
			case RED:
				putc(0, out);
				putc(0, out);
				putc(255, out);
				break;
			case GREEN:
				putc(0, out);
				putc(255, out);
				putc(0, out);
				break;
			case YELLOW:
				putc(0, out);
				putc(255, out);
				putc(255, out);
				break;
			case BLUE:
				putc(255, out);
				putc(0, out);
				putc(0, out);
				break;
			case MAGENTA:
				putc(255, out);
				putc(0, out);
				putc(255, out);
				break;
			case CYAN:
				putc(255, out);
				putc(255, out);
				putc(0, out);
				break;
			case WHITE:
				putc(255, out);
				putc(255, out);
				putc(255, out);
				break;
			}
		}
		for (l = 1; l <= (size_x % 4); l++) {
			putc(0, out);
		}
	}

	fclose(out);
	clearscr();
	printf("%s has been saved.\n", name);
}

// scaler //
void scaler(int scale){
	printf("ok") ;
	int i, k, s, m, cx, cy;
	int new_size_x = size_x*scale;	
	int new_size_y = size_y*scale;	
	char **new_map = (char**) malloc(new_size_y * sizeof(char*));
	for (i = 0; i < new_size_y; i++) {
		new_map[i] = (char*) malloc(new_size_x * sizeof(char));
	}
	
	s = -1;
	for (i = 0; i < size_y; i++) {
		cy = 1;
		while(cy <= scale){
			s++;	
			m = -1;
			for (k = 0; k < size_x; k++) {
				cx = 1;
				while(cx <= scale){
						m++;
						new_map[s][m] = map[i][k];
						cx++;
				}
			}
		cy++;
		}
	}
	
	for (i = 0; i < size_y; i++)
			free(map[i]);
	free(map);
	map = new_map;
	size_x = new_size_x;
	size_y = new_size_y;
}

// savedetails //
void savedetails() {
	char name[64];
	FILE *out;
	int scale;

	clearscr();
	while (1) {
		printf("save image as (relative path is allowed): ");
		scanf("%s", name);
		if (!strsuffix(name, ".bmp"))
			strcat(name, ".bmp");
		if ((out = fopen(name, "r")) != NULL) {
			printf("the file already exists.\n\n");
			fclose(out);
		}else{
			break;
		}
	}

	clearscr();
	while (1) {
		printf("scale rate (recommended is 20): ");
		scanf("%d", &scale); // it can be some string input, but the scale is always between 1 and 500
		if (scale < 1 || scale > 500) {
			printf("1 <= scale <= 500\n\n");
		} else {
			break;
		}
	}

	scaler(scale);
	save(name);
}

// drawing  //
void drawing() {
	while (1) {
		int end = 0;
		clearscr();
		mapdrawer();
		bottom();
		rightside();
		char pressed = lgetch();
		if (pressed == 'l') {
			clearscr();
			while(1){
					printf("exit without saving? (y/n)\n");
					char c = lgetch();
					if (c == 'y') {
						end = 1;
						break;
					}else if (c == 'n') {
						savedetails();
						end = 1;
						break;
					}else {
						printf("invalid character\n\n");
					}
			}
			if (end)
				break;
		}
		if (pressed == 's') {
			if (x < size_y - 1) {
				x++;
				if (continous)
					map[x][y] = actual_color;
			}
		} else if (pressed == 'w') {
			if (x > 0) {
				x--;
				if (continous)
					map[x][y] = actual_color;
			}
		} else if (pressed == 'a') {
			if (y > 0) {
				y--;
				if (continous)
					map[x][y] = actual_color;
			}
		} else if (pressed == 'd') {
			if (y < size_x - 1) {
				y++;
				if (continous)
					map[x][y] = actual_color;
			}
		} else if (pressed == 10) {
			map[x][y] = actual_color;
		} else if (pressed == 'q') {
			map[x][y] = WHITE;
		} else if (pressed == 'm') {
			savedetails();
			break;
		} else if (pressed == 'r') {
			rotatecolor();
		} else if (pressed == 'e') {
			continous = !continous;
			map[x][y] = actual_color;
		}
	}
}

//  main  //
int main(void) {
	int i, k;
	clearscr();
	printf("lieditor 1.1\nadam.laszlo.91@gmail.com\n2014. 1. august\n\n");
	while (1) {
		printf("image dimensions\n");
		printf("image width: ");
		scanf("%d", &size_x);
		flushstdin(); // needed for in case of invalid character input
		printf("image height: ");
		scanf("%d", &size_y);
		flushstdin(); // needed for in case of invalid character input
		if (size_x < 3 || size_x > 70 || size_y < 3 || size_y > 20) {
			printf("allowed dimensions: 3x3 -> 70x20\n\n");
		} else {
			break;
		}
	}

	map = (char**) malloc(size_y * sizeof(char*));
	for (i = 0; i < size_y; i++) {
		map[i] = (char*) malloc(size_x * sizeof(char));
	}
	for (i = 0; i < size_y; i++) {
		for (k = 0; k < size_x; k++) {
			map[i][k] = WHITE;
		}
	}

	drawing();

	for (i = 0; i < size_y; i++) {
		if (map[i] == NULL)
			free(map[i]);
	}
	free(map);
	

	return 0;
}

